/*!
    \file  netconf.c
    \brief network connection configuration 
*/

/*
    Copyright (C) 2016 GigaDevice

    2016-10-19, V1.0.0, demo for GD32F4xx
*/

#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/tcp.h"
#include "lwip/udp.h"
#include "netif/etharp.h"
#include "lwip/dhcp.h"
#include "ethernetif.h"
#include "stdint.h"
#include "../main.h"
#include "netconf.h"
#include <stdio.h>
#include <string.h>
#include "tcp_impl.h"
#include "system_config/system_config.h"
#include "project_config/project_config.h"

#define MAX_DHCP_TRIES        4

typedef enum {
    DHCP_START = 0,
    DHCP_WAIT_ADDRESS,
    DHCP_ADDRESS_ASSIGNED,
    DHCP_TIMEOUT
} dhcp_state_enum;

#ifdef USE_DHCP
uint32_t dhcp_fine_timer = 0;
uint32_t dhcp_coarse_timer = 0;
dhcp_state_enum dhcp_state = DHCP_START;
#endif /* USE_DHCP */

struct netif g_mynetif;
uint32_t tcp_timer = 0;
uint32_t arp_timer = 0;
uint32_t ip_address = 0;

static SYSTEM_CONFIG_T *system_config;

void lwip_dhcp_process_handle(void);

/*!
    \brief      initializes the LwIP stack
    \param[in]  none
    \param[out] none
    \retval     none
*/
void lwip_stack_init(void) {
    struct ip_addr ipaddr;
    struct ip_addr netmask;
    struct ip_addr gw;
    system_config = system_config_get();

    /* initializes the dynamic memory heap defined by MEM_SIZE */
    mem_init();

    /* initializes the memory pools defined by MEMP_NUM_x */
    memp_init();
#ifdef TIMEOUT_CHECK_USE_LWIP
    sys_timeouts_init();
#endif /* TIMEOUT_CHECK_USE_LWIP */

#ifdef USE_DHCP
    if (system_config->dhcp_enabled) {
        ipaddr.addr = 0;
        netmask.addr = 0;
        gw.addr = 0;
    } else
#endif
    {
        ipaddr = system_config->ip;
        netmask = system_config->netmask;
        gw = system_config->netmask;
    }

    netif_add(&g_mynetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);

    /* registers the default network interface */
    netif_set_default(&g_mynetif);

    /* when the netif is fully configured this function must be called */
    netif_set_up(&g_mynetif);
}

/*!
    \brief      called when a frame is received
    \param[in]  none
    \param[out] none
    \retval     none
*/
void lwip_pkt_handle(void) {
    /* read a received packet from the Ethernet buffers and send it to the lwIP for handling */
    ethernetif_input(&g_mynetif);
}

/*!
    \brief      LwIP periodic tasks
    \param[in]  localtime the current LocalTime value
    \param[out] none
    \retval     none
*/
void lwip_periodic_handle(__IO uint32_t localtime) {
#if LWIP_TCP
    /* TCP periodic process every 250 ms */
    if (localtime - tcp_timer >= TCP_TMR_INTERVAL) {
        tcp_timer = localtime;
        tcp_tmr();
    }

#endif /* LWIP_TCP */

    /* ARP periodic process every 5s */
    if ((localtime - arp_timer) >= ARP_TMR_INTERVAL) {
        arp_timer = localtime;
        etharp_tmr();
    }

#ifdef USE_DHCP
    if (system_config->dhcp_enabled) {
        /* fine DHCP periodic process every 500ms */
        if (localtime - dhcp_fine_timer >= DHCP_FINE_TIMER_MSECS) {
            dhcp_fine_timer = localtime;
            dhcp_fine_tmr();
            if ((DHCP_ADDRESS_ASSIGNED != dhcp_state) && (DHCP_TIMEOUT != dhcp_state)) {
                /* process DHCP state machine */
                lwip_dhcp_process_handle();
            }
        }

        /* DHCP coarse periodic process every 60s */
        if (localtime - dhcp_coarse_timer >= DHCP_COARSE_TIMER_MSECS) {
            dhcp_coarse_timer = localtime;
            dhcp_coarse_tmr();
        }
    }
#endif /* USE_DHCP */
}

#ifdef USE_DHCP

/*!
    \brief      lwip_dhcp_process_handle
    \param[in]  none
    \param[out] none
    \retval     none
*/
void lwip_dhcp_process_handle(void) {
    struct ip_addr ipaddr;
    struct ip_addr netmask;
    struct ip_addr gw;

    switch (dhcp_state) {
        case DHCP_START:
            dhcp_start(&g_mynetif);
            ip_address = 0;
            dhcp_state = DHCP_WAIT_ADDRESS;
            break;

        case DHCP_WAIT_ADDRESS:
            /* read the new IP address */
            ip_address = g_mynetif.ip_addr.addr;
            char mode[8] = {0};

            if (0 != ip_address) {
                dhcp_state = DHCP_ADDRESS_ASSIGNED;
                /* stop DHCP */
                dhcp_stop(&g_mynetif);
                strcpy(mode, "DHCP");
            } else {
                /* DHCP timeout */
                if (g_mynetif.dhcp->tries > MAX_DHCP_TRIES) {
                    dhcp_state = DHCP_TIMEOUT;
                    /* stop DHCP */
                    dhcp_stop(&g_mynetif);

                    /* static address used */
                    ipaddr = system_config->ip;
                    netmask = system_config->netmask;
                    gw = system_config->netmask;
                    netif_set_addr(&g_mynetif, &ipaddr, &netmask, &gw);
                    strcpy(mode, "static");
                }
            }
            printf("\r\nmode %s -- ip address: %d.%d.%d.%d \r\n", mode,
                   ip4_addr1_16(&ip_address), ip4_addr2_16(&ip_address), ip4_addr3_16(&ip_address),
                   ip4_addr4_16(&ip_address));
            break;

        default:
            break;
    }
}

#endif /* USE_DHCP */

unsigned long sys_now(void) {
    extern volatile unsigned int g_localtime;
    return g_localtime;
}

void netconf_switch_mode(uint32_t dhcp_enabled) {
    if (dhcp_enabled) {
        dhcp_state = DHCP_START;
        g_mynetif.dhcp->tries = 0;
    } else {
        dhcp_state = DHCP_TIMEOUT;
        g_mynetif.dhcp->tries = MAX_DHCP_TRIES;
    }
}

